package com.ece.core.versioning;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.internal.util.SVNURLUtil;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.ece.core.config.IParserCallback;
import com.ece.core.config.XMLConfigParser;
import com.ece.core.config.XMLParserFactory;
import com.ece.core.versioning.beans.MappingBean;
import com.ece.core.versioning.beans.RevisionMappingBean;
import com.ece.core.versioning.beans.VersioningInfoBean;

public class MapFilesParser {

	XMLParserFactory xpf = new XMLParserFactory();

	XMLConfigParser xcp = XMLParserFactory.getParser(Activator.PLUGIN_ID);

	public Document parseXmlFile(File parentDirectory) {
		// get the factory
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		Document dom = null;
		xcp.parse(parentDirectory, new IParserCallback() {

			@Override
			public String getInitialFileContent() {
				return "<?xml version='1.0' encoding='UTF-8'?>"
						+ "<versioningInfo>" + "</versioningInfo>";
			}
		});

		try {

			// Using factory get an instance of document builder
			DocumentBuilder db = dbf.newDocumentBuilder();

			// parse using builder to get DOM representation of the XML file
			dom = db.parse(parentDirectory);

		} catch (ParserConfigurationException pce) {
			pce.printStackTrace();
		} catch (SAXException se) {
			se.printStackTrace();
		} catch (IOException ioe) {
			ioe.printStackTrace();
		}

		return dom;
	}

	public VersioningInfoBean parseDocument(Document dom) {
		// get the root element
		VersioningInfoBean versioningInfoBean = new VersioningInfoBean();
		Element docEle = dom.getDocumentElement();

		// get a nodelist of versioningInfo elements
		NodeList nl = docEle.getElementsByTagName("versioningInfo");
		if (nl != null && nl.getLength() > 0) {
			for (int i = 0; i < nl.getLength(); i++) {
				try {

					// get the target server element
					Element versioningInfoElement = (Element) nl.item(i);
					NodeList targetServerNodeList = versioningInfoElement
							.getElementsByTagName("targetServer");
					Element targetServerElement = (Element) targetServerNodeList
							.item(0);
					String targetPathInfo = targetServerElement
							.getAttribute("path");
					SVNURL targetPath;

					targetPath = SVNURL.parseURIDecoded(targetPathInfo);

					versioningInfoBean.setTargetServerURL(targetPath);

					// get the oc server element
					NodeList ocServerNodeList = versioningInfoElement
							.getElementsByTagName("ocServer");
					Element ocServerElement = (Element) ocServerNodeList
							.item(0);
					String ocPathInfo = ocServerElement.getAttribute("path");
					SVNURL ocPath = SVNURL.parseURIDecoded(ocPathInfo);
					versioningInfoBean.setOcServerURL(ocPath);
				} catch (SVNException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}

				// get the mapping element nodelist
				NodeList list = docEle.getElementsByTagName("revisionMapping");
				List<RevisionMappingBean> revisionMappingBeans = new ArrayList<RevisionMappingBean>();
				if (list != null && list.getLength() > 0) {

					for (int i1 = 0; i1 < nl.getLength(); i1++) {
						try {
							RevisionMappingBean mappingBean = new RevisionMappingBean();
							Element revisionMappingElement = (Element) list
									.item(i1);
							MappingBean target = new MappingBean();
							Element targetMappingInfo = (Element) revisionMappingElement
									.getElementsByTagName("target");

							String mappingTargetPath = getTextValue(
									targetMappingInfo, "targetPath");
							SVNURL targetFilePath;

							targetFilePath = SVNURL
									.parseURIDecoded(mappingTargetPath);

							target.setFilePath(targetFilePath);
							int mappingTargetRevision = getIntValue(
									targetMappingInfo, "targetRevision");
							SVNRevision targetFileRevision = SVNRevision
									.create(mappingTargetRevision);
							target.setRevisionNumber(targetFileRevision);

							MappingBean oc = new MappingBean();
							Element ocMappingInfo = (Element) revisionMappingElement
									.getElementsByTagName("oc");

							String mappingOCPath = getTextValue(ocMappingInfo,
									"ocPath");
							SVNURL ocFilePath = SVNURL
									.parseURIDecoded(mappingOCPath);
							oc.setFilePath(ocFilePath);
							int mappingOCRevision = getIntValue(ocMappingInfo,
									"ocRevision");
							SVNRevision ocFileRevision = SVNRevision
									.create(mappingOCRevision);
							oc.setRevisionNumber(ocFileRevision);
							mappingBean.setTarget(target);
							mappingBean.setOc(oc);
							revisionMappingBeans.add(mappingBean);
						} catch (SVNException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}

					}
				}

				versioningInfoBean.setRevisionMappingInfo(revisionMappingBeans);
			}
		}
		return versioningInfoBean;
	}

	private String getTextValue(Element ele, String tagName) {
		String textVal = null;
		NodeList nl = ele.getElementsByTagName(tagName);
		if (nl != null && nl.getLength() > 0) {
			Element el = (Element) nl.item(0);
			textVal = el.getFirstChild().getNodeValue();
		}

		return textVal;
	}

	private int getIntValue(Element ele, String tagName) {
		// in production application you would catch the exception
		return Integer.parseInt(getTextValue(ele, tagName));
	}

	// Methods to insert the data into the xml file

	/**
	 * @returns the root element of the file
	 */
	private Element getVersioningInfoRootElement(File parentDirectory) {

		Document versioningInfoDom = parseXmlFile(parentDirectory);
		Element versioningInfo = (Element) versioningInfoDom
				.getElementsByTagName("versioningInfo");

		return versioningInfo;
	}

	/**
	 * @returns the dom object after setting the target server path
	 */
	public Document setTargetServerName(File parentDirectory,
			String targetServerPath) {

		Document dom = parseXmlFile(parentDirectory);
		Element versioningInfo = (Element) dom
				.getElementsByTagName("versioningInfo");
		Element targetServer = dom.createElement("targetServer");
		targetServer.setAttribute("path", targetServerPath);
		versioningInfo.appendChild(targetServer);

		return dom;

	}

	/**
	 * @returns the dom object after setting the oc server path
	 */
	public Document setOCServerName(File parentDirectory, String ocServerPath) {

		Document dom = parseXmlFile(parentDirectory);
		Element versioningInfo = (Element) dom
				.getElementsByTagName("versioningInfo");
		Element ocServer = dom.createElement("ocServer");
		ocServer.setAttribute("path", ocServerPath);
		versioningInfo.appendChild(ocServer);

		return dom;
	}

	/**
	 * @returns the dom object after setting the revision mapping information
	 */

	public Document setRevisionMappingInfo(File parentDirectory,
			List<RevisionMappingBean> revisionMappingInfo) {

		Document dom = parseXmlFile(parentDirectory);
		Element versioningInfo = (Element) dom
				.getElementsByTagName("versioningInfo");

		Iterator<RevisionMappingBean> iterator = revisionMappingInfo
				.listIterator();

		while (iterator.hasNext()) {
			RevisionMappingBean revisionMappingBeanbean = iterator.next();
			Element revisionMapping = dom.createElement("revisionMapping");
			Element target = dom.createElement("target");
			Element oc = dom.createElement("oc");
			Element targetPath = dom.createElement("targetPath");
			Element targetRevision = dom.createElement("targetRevision");
			Element ocPath = dom.createElement("ocPath");
			Element ocRevision = dom.createElement("ocRevision");

			targetPath.setNodeValue(revisionMappingBeanbean.target
					.getFilePath().toString());
			targetRevision.setNodeValue(revisionMappingBeanbean.target
					.getRevisionNumber().toString());
			target.appendChild(targetPath);
			target.appendChild(targetRevision);

			ocPath.setNodeValue(revisionMappingBeanbean.oc.getFilePath()
					.toString());
			ocRevision.setNodeValue(revisionMappingBeanbean.oc
					.getRevisionNumber().toString());
			oc.appendChild(ocPath);
			oc.appendChild(ocRevision);

			revisionMapping.appendChild(target);
			revisionMapping.appendChild(oc);

			versioningInfo.appendChild(revisionMapping);

		}

		return dom;
	}

	private void saveToFileSystem(Document dom, File filePath) {

		try {
			// print
			OutputFormat format = new OutputFormat(dom);
			format.setIndenting(true);

			// to generate output to console use this serializer
			// XMLSerializer serializer = new XMLSerializer(System.out, format);

			// to generate a file output use fileoutputstream instead of
			// system.out
			XMLSerializer serializer = new XMLSerializer(new FileOutputStream(
					new File(filePath + "\\.map")), format);

			serializer.serialize(dom);

		} catch (IOException ie) {
			ie.printStackTrace();
		}
	}

}
